unit tags;
{$IFDEF FPC}
{$MODE DELPHI}
{$ENDIF}
interface
uses BASS, SysUtils;
const _ID3 = 1;
      _APE = 2;
      _BEX = 3;
      _WAV = 4;
      _MP4 = 5;
      _OGG = 6;
type Tf_info =
record
   filetype: byte;
   tags: pointer;
   file_path: pwidechar;
   length: Float;
end;

Type PL_Tags = record
  title: pwidechar;
  album: pwidechar;
  artist: pwidechar;
  year: pwidechar;
  genre: pwidechar;
  number: longint;
end;

PTf_info = ^Tf_Info;
P_ID3 = ^TAG_ID3;
P_APE = ^TAG_APE_BINARY;
P_BEXT = ^TAG_BEXT;

function Pl_Add(const flnm: widestring): PTf_info;
function Unname_tags(const tgs: Pointer; const bt: Byte): PL_TAGS;

implementation
uses  Adaptors, utils, trans_ru, Math, adapt_consts;
var
  str_vector: array of WideString;
  str_v_size, str_v_real_size: Cardinal;

  id3_vector: array of ENGINE_ID3;
  id3_v_size, id3_v_real_size: Cardinal;

  ape_vector: array of ENGINE_APE;
  ape_v_size, ape_v_real_size: Cardinal;

  bext_vector: array of ENGINE_bext;
  bext_v_size, bext_v_real_size: Cardinal;

function add_string(const str: widestring): PwideChar;
var
  res: PwideChar;
  procedure v_add(const str: widestring);
  begin
    inc(str_v_size);
    if (str_v_size > str_v_real_size) then
    begin
      str_v_real_size := Max(str_v_real_Size * 2, 1);
      SetLength(str_vector, str_v_real_size);
    end;
    str_vector[str_v_size - 1] := str;
  end;
begin
  v_add(str);
  res := PwideChar(str_vector[str_v_size - 1]);
  add_string := res;
end;

function   tgID3_vector_add(const pnt: pointer): Pointer;
  procedure v_add(const obj: ENGINE_ID3);
  begin
    inc(id3_v_size);
    if (id3_v_size > id3_v_real_size) then
    begin
      id3_v_real_size := Max(id3_v_real_size * 2, 1);
      SetLength(id3_vector, id3_v_real_size);
    end;
    id3_vector[id3_v_real_size - 1] := obj;
  end;
var
  id3: P_ID3;
begin
  if (pnt = nil) then exit;
  id3 := pnt;
  v_add(id3^);
  tgID3_vector_add := @id3_vector[id3_v_real_size - 1];
end;

function   tgAPE_vector_add(const pnt: pointer): Pointer;
  procedure v_add(const obj: ENGINE_APE);
  begin
    inc(ape_v_size);
    if (ape_v_size > ape_v_real_size) then
    begin
      ape_v_real_size := Max(ape_v_real_size * 2, 1);
      SetLength(ape_vector, ape_v_real_size);
    end;
    ape_vector[ape_v_real_size - 1] := obj;
  end;
var
  ape: P_ape;
begin
  if (pnt = nil) then exit;
  ape := pnt;
  v_add(ape^);
  tgape_vector_add := @ape_vector[ape_v_real_size - 1];
end;

function   tgbext_vector_add(const pnt: pointer): Pointer;
  procedure v_add(const obj: ENGINE_bext);
  begin
    inc(bext_v_size);
    if (bext_v_size > bext_v_real_size) then
    begin
      bext_v_real_size := Max(bext_v_real_size * 2, 1);
      SetLength(bext_vector, bext_v_real_size);
    end;
    bext_vector[bext_v_real_size - 1] := obj;
  end;
var
  bext: P_bext;
begin
  if (pnt = nil) then exit;
  bext := pnt;
  v_add(bext^);
  tgbext_vector_add := @bext_vector[bext_v_real_size - 1];
end;

function  _DETECT(const flnm: AnsiString): byte;
var
  d: string;
  i: Cardinal;
begin
  i := length(flnm);
  d := '';
  while (i > 0) and (flnm[i] <> '.') do begin
   if (flnm[i] in ['a'..'z']) then
      d := chr(ord(flnm[i]) - (ord('a')-ord('A'))) + d
   else
    d := flnm[i] + d;
    dec(i);
  end;
  if (d = 'APE') then
    _DETECT := _APE;
  if (d = 'MP3') then
    _DETECT := _ID3;
  if (d = 'BEX') then
    _DETECT := _BEX;
  if (d = 'OGG') then
    _DETECT := _ID3;
  if (d = 'M4A') then
    _DETECT := _ID3;
  if (d = 'WAV') then
    _DETECT := _ID3;
end;

function ANSI_WIDE(const str: AnsiString): PwideChar;
var
  wstr: widestring;
begin
  wstr := str;
  ANSI_WIDE := @wstr;
end;

function WIDE_ANSI(const str: Widestring): PAnsiChar;
var
  ans: Ansistring;
begin
  ans := str;
  WIDE_ANSI := @ans;
end;

function PW(const str: PwideChar): WideString;
begin
  pw := str;
end;

function unname_Id3(const tgs: Pointer): PL_TAGS;
var
  pt: P_ID3;
  tags: Pl_TAGS;

begin
  if (tgs = nil) then exit;
  pt := tgs;
  tags.title := add_String((pt^.title));
  tags.album := add_String((pt^.album));
  tags.year := add_String((pt^.year));
  tags.artist := add_String((pt^.artist));
  //tags.number := strtoint(pt^.id);
  Unname_ID3 := tags;
end;

function Unname_APE(const tgs: Pointer): PL_TAGS;
var
  pt: P_APE;
  tags: PL_TAGS;
begin
  pt := tgs;
  fillchar(tags, sizeof(tags), 0);
  tags.title:= ANSI_WIDE(pt^.data);
  tags.number := strtoint(pt^.key);
  Unname_Ape := tags;
end;

function Unname_BEXT(const tgs: Pointer): Pl_TAGS;
var
  pt: P_BEXT;
  tags: PL_TAGS;
begin
  pt := tgs;
  fillchar(tags, sizeof(tags), 0);
  tags.title := ANSI_WIDE(pt^.Description);
  tags.year := ANSI_WIDE(pt^.OriginationDate);
  unname_BEXT := tags;
end;

function unname_tags(const tgs: Pointer; const bt: Byte): PL_TAGS;
begin
  if (tgs = nil) then
  begin
    _ERROR(_TE_INVALID_TAGS);
    exit;
  end;
  case bt of
    _ID3: Unname_tags := unname_Id3(tgs);
    _APE: Unname_tags := unname_APE(tgs);
    _BEX: Unname_tags := unname_BEXT(tgs);
  end;
end;


function Get_TAGS(const hndl: Hstream; const bt: Byte): Pointer;
var
  id3: P_id3;
  ape: P_APE;
  bex: P_BEXT;
  pnt: Pointer;
begin
  case bt of
    _ID3: begin
            pnt := bass_channelGetTags(hndl,BASS_TAG_ID3);
            Get_TAGs := tgID3_vector_add(Pnt);
            exit;
          end;
    _APE: begin
            pnt := bass_channelGetTags(hndl, BASS_TAG_APE);
            Get_TAGS := tgAPE_vector_add(Pnt);
            exit;
          end;
    _BEX: begin
             pnt := Bass_channelGetTags(hndl, BASS_TAG_RIFF_BEXT);
             Get_TAGS := tgBEXt_vector_add(Pnt);
             exit;
           end;
    _WAV: begin
             pnt := Bass_channelGetTags(hndl, BASS_TAG_WAVEFORMAT);
             GET_TAGS := pnt;
             exit;
           end;
    _OGG: begin
             pnt := Bass_channelGetTags(hndl, BASS_TAG_OGG);
             Get_TAGS := pnt;
             exit;
           end;
    _MP4: begin
             pnt := Bass_channelGetTags(hndl, BASS_TAG_MP4);
             Get_TAGS := pnt;
             exit;
           end;
  end;
end;


function Pl_Add(const flnm: Widestring): PTf_Info;
var
  id3tag: TAG_ID3;
  d_b: Byte;
  File_Info: Tf_Info;
  hndl: Hstream;
  Pf: PTf_Info;
  str: WideString;
begin
  hndl := ENGINE_create_channel(flnm, true);
  if (hndl = 0) then
  begin
    _ERROR(_TE_STREAM_NOT_CREATED);
    Pl_add := nil;
    exit;
  end;
  repeat
    File_Info.length := ENGINE_Get_Length(hndl);
    File_Info.file_path := Add_String(flnm);
    File_Info.filetype := _DETECT(flnm);
    FIle_Info.tags := Get_TAGS(hndl, File_Info.filetype);
    ENGINE_Terminate_Channel(hndl);
    Pf := @FILE_INFO;
    str := PW(FILE_INFO.file_path);
    until Pf^.file_path = flnm;

  PL_Add := Pf;
end;
initialization
  str_v_size := 0;
  id3_v_size := 0;
  ape_v_size := 0;
  bext_v_size := 0;
end.